home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #15
/
Monster Media Number 15 (Monster Media)(July 1996).ISO
/
netmail
/
cpt152.zip
/
CPT-S152.ZIP
/
CPT_CODE.PAS
next >
Wrap
Pascal/Delphi Source File
|
1996-05-16
|
36KB
|
1,139 lines
{$N-,E- no math support needed}
{$X- function calls may not be discarded}
{$I- disable I/O checking (trap errors by checking IOResult)}
UNIT CPT_CODE;
INTERFACE
{$IFDEF DPMI}
USES DOS, NUMDAYS, ARCID;
{$ELSE}
USES DOS, NUMDAYS, ARCID, HEAPMAN;
{$ENDIF}
TYPE
MemLink = ^MemberRec;
MemberRec = RECORD
Name : STRING [25];
sent : WORD;
oldest,
newest : STRING [8];
BBS1,
BBS2 : STRING [79];
notes : STRING [79];
next : MemLink;
END;
CONST
version = ' v1.52 ';
author = 'Copyright (c) May 16th, 1996, by David Daniel Anderson - Reign Ware.';
OldDelimitLine = '=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=' +
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=';
DelimitLine = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' +
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~';
EndOfDB = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' +
' end of database ' +
'<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
High_Message : STRING [7] = '';
cursorState : BYTE = 1; {0..3}
cursorData : ARRAY [0..3] OF CHAR = (#179, #47, #196, #92);
DATFileName = 'MESSAGES.DAT';
CNFFileName = 'CONTROL.DAT';
lf = #13#10;
VAR confnumb : WORD;
field : STRING;
inverse : BOOLEAN;
VAR
unQWK, unARC, unARJ, unHAP, unLHA,
unPAK, unRAR, unUC2, unZIP, unZOO : PATHSTR;
ExCMD : PATHSTR;
CheckFROM,
Validate,
TrackPrivate : BOOLEAN;
CONFname : STRING [25];
{===========================================================================}
PROCEDURE WriteError (CONST problem: BYTE);
FUNCTION WordToHex (i: WORD): STRING;
PROCEDURE CheckIO;
PROCEDURE cursorOff;
PROCEDURE cursorOn;
PROCEDURE updateCursor;
FUNCTION WhereX: BYTE;
FUNCTION WhereY: BYTE;
PROCEDURE GotoXY (X, Y: BYTE);
PROCEDURE WriteCharAtCursor (X: CHAR);
PROCEDURE ClrEol;
PROCEDURE WriteMemAvail;
FUNCTION IsFile (CONST FileName: PATHSTR): BOOLEAN;
PROCEDURE EraseFile (CONST FileName : STRING);
(* PROCEDURE UpFast (VAR Str: STRING); {** from SWAG **} *)
FUNCTION UpStr (lstr : STRING): STRING;
FUNCTION LowerStr (w: STRING): STRING;
FUNCTION MixCase (s: STRING): STRING;
FUNCTION RTrim (InStr: STRING): STRING;
FUNCTION LTrim (InStr: STRING): STRING;
FUNCTION Squeeze (ss: STRING): STRING;
Function LongIntDays (DayStr: String): LongInt;
FUNCTION GetNewHigh (High, current: STRING): STRING;
FUNCTION MiddleOf (CONST s: STRING): STRING;
FUNCTION GetOriginLine (ol : STRING): STRING;
FUNCTION GetConfNUMBER (CONST PSTR: STRING): PATHSTR;
FUNCTION IsDir (CONST FileName: PATHSTR): BOOLEAN;
FUNCTION GetFilePath (CONST PSTR: STRING; VAR sDir: DIRSTR): PATHSTR;
FUNCTION GetCONFname (CONST QWKpath, CNFFileName: STRING): STRING;
FUNCTION BuildList (VAR list: MemLink; CONST fname: STRING): WORD;
FUNCTION ReadDAT (VAR list: MemLink; CONST DATFileName: STRING): WORD;
FUNCTION Relevant (CONST s: STRING; CONST len: BYTE): STRING;
PROCEDURE GetSortField (CONST PSTR: STRING);
FUNCTION CompareFields (CONST cnode, cnode2: MemLink): BOOLEAN;
PROCEDURE SortLinkedList (VAR list: MemLink); {By Ian Lin, found in SWAG}
PROCEDURE WriteList (VAR list: MemLink; CONST fname: STRING; CONST mems: WORD);
PROCEDURE WriteStats (VAR list: MemLink; CONST fname: STRING; CONST mems: WORD);
PROCEDURE InitCONFIG;
FUNCTION IsArchive (CONST SomeFile: PATHSTR): PATHSTR;
FUNCTION ExtractFile (CONST ArchiveFile, FileToEx: PATHSTR; ExCMD: PATHSTR): BOOLEAN;
{===========================================================================}
IMPLEMENTATION
PROCEDURE WriteError (CONST problem: BYTE);
VAR
message: STRING [79];
BEGIN
CASE problem OF
1 : message := 'Invalid parameter on command line or parameter missing.';
2 : message := 'No files found. First parameter must be a valid file specification.';
3 : message := 'You cannot use ".STT" as the file extension, since .STT is used by CPT-Stat.';
(* Numbers 4 and 5 are -possible- reasons for aborting, but I've chosen not to. *)
(* 4 : message := 'Configuration file not found with executable. Consult the documentation.'; *)
(* 5 : message := 'Unable to run unarchiver! Aborting.'; *)
6 : message := 'You cannot just specify a path, add "*.*" or "\*.*" for all files.';
7 : message := 'File handling error. Original has not been updated, and is possibly corrupt.';
8 : message := 'This database was corrupted by CPT v1.36, read the "CPT-Fix.DOC" file for help.';
ELSE message := 'Unknown error.';
END;
WriteLn (#7, 'Error encountered, number ', problem, ':'); WriteLn (message);
END;
FUNCTION WordToHex (i: WORD): STRING; {Convert a WORD variable to STRING}
CONST
HexLetters : ARRAY [0..15] OF CHAR = '0123456789ABCDEF';
BEGIN
WordToHex := Concat (HexLetters [Hi (i) SHR 4], HexLetters [Hi (i) AND 15],
HexLetters [Lo (i) SHR 4], HexLetters [Lo (i) AND 15]);
END;
PROCEDURE CheckIO;
BEGIN
IF IOResult <> 0 THEN Halt (7);
END;
PROCEDURE cursorOff; ASSEMBLER;
(* Routine from SWAG *)
ASM
mov AH, 3; mov BH, 0; Int $10; OR CH, $20; mov AH, 1; Int $10;
END;
PROCEDURE cursorOn; ASSEMBLER;
(* Routine from SWAG *)
ASM
mov AH, 3; mov BH, 0; Int $10; AND CH, NOT $20; mov AH, 1; Int $10;
END;
PROCEDURE updateCursor;
BEGIN
cursorState := Succ (cursorState) AND 3;
Write (cursorData [cursorState], ^H);
END;
FUNCTION WhereX: BYTE; ASSEMBLER;
(* Routine from SWAG *)
ASM
MOV AH, 3 {Ask For current cursor position}
MOV BH, 0 { On page 0 }
Int 10h { Return inFormation in DX }
Inc DL { Bios Assumes Zero-based. Crt.WhereX Uses 1 based }
MOV AL, DL { Return X position in AL For use in Byte Result }
END;
FUNCTION WhereY: BYTE; ASSEMBLER;
(* Routine from SWAG *)
ASM
MOV AH, 3 {Ask For current cursor position}
MOV BH, 0 { On page 0 }
Int 10h { Return inFormation in DX }
Inc DH { Bios Assumes Zero-based. Crt.WhereY Uses 1 based }
MOV AL, DH { Return Y position in AL For use in Byte Result }
END;
PROCEDURE GotoXY (X, Y: BYTE); ASSEMBLER;
(* Routine from SWAG *)
ASM
MOV DH, Y { DH = Row (Y) }
MOV DL, X { DL = Column (X) }
Dec DH { Adjust For Zero-based Bios routines }
Dec DL { Turbo Crt.GotoXY is 1-based }
MOV BH, 0 { Display page 0 }
MOV AH, 2 { Call For SET CURSOR POSITION }
Int 10h
END;
PROCEDURE WriteCharAtCursor (X: CHAR);
(* Routine from SWAG *)
VAR
reg: REGISTERS;
BEGIN
reg. AH := $0A;
reg. AL := Ord (X);
reg. BH := $00; {* Display Page Number. * for Graphics Modes! *}
reg. CX := 1; {* Word for number of characters to write *}
Intr ($10, reg);
END;
PROCEDURE ClrEol;
(* Routine by DDA *)
VAR
NumCol: WORD ABSOLUTE $0040: $004A; { Number of CRT columns (1-based) }
X, Y, DistanceToRight: BYTE;
BEGIN
X := WhereX;
Y := WhereY;
DistanceToRight := NumCol - X;
Write ('': DistanceToRight);
WriteCharAtCursor (#32);
GotoXY (X, Y);
END;
PROCEDURE WriteMemAvail;
BEGIN
GotoXY (60, WhereY);
WriteLn ('Free RAM: ', MemAvail);
END;
FUNCTION IsFile (CONST FileName: PATHSTR): BOOLEAN;
VAR
Attr : WORD;
cFile : FILE;
BEGIN
Assign (cFile, FileName);
GetFAttr (cFile, Attr);
IF (DosError = 0) AND ((Attr AND Directory) <> Directory)
THEN IsFile := TRUE
ELSE IsFile := FALSE;
END;
PROCEDURE EraseFile (CONST FileName : STRING);
VAR
cFile : FILE;
BEGIN
IF IsFile (FileName) THEN BEGIN
Assign (cFile, FileName);
SetFAttr (cFile, 0);
Erase (cFile); CheckIO;
END;
END;
PROCEDURE UpFast (VAR Str: STRING); {** from SWAG **}
INLINE ($8C/$DA/$5E/$1F/$FC/$AC/$30/$E4/$89/$C1/$E3/$12/$BB/Ord('a')/Ord('z')/
$AC/$38/$D8/$72/$08/$38/$F8/$77/$04/$80/$6C/$FF/$20/$E2/$F1/$8E/$DA);
FUNCTION UpStr (lstr : STRING): STRING;
BEGIN
upfast (lstr);
UpStr := lstr;
END;
FUNCTION LowerStr (w: STRING): STRING;
VAR
cp : INTEGER; {The position of the character to change.}
BEGIN
FOR cp := 1 TO Length (w) DO
IF w [cp] in ['A'..'Z'] THEN
System.Inc (w [cp], 32);
LowerStr := w;
END;
FUNCTION MixCase (s: STRING): STRING;
CONST
space = #32;
hyphen = #45;
period = #46;
VAR
cp : INTEGER; {The position of the character to change.}
s2 : STRING;
BEGIN
s := LowerStr(s);
s [1] := UpCase (s [1]); { Capitalize first letter }
s2 := '';
WHILE Pos (space, s) > 0 DO BEGIN { Capitalize initial letters after spaces }
s2 := s2 + Copy (s, 1, (Pos (space, s)));
Delete (s, 1, (Pos (space, s)));
s [1] := UpCase (s [1]);
END;
IF (Length (s) >= 3) AND (Copy (s, 1, 2) = 'Mc') THEN
s [3] := UpCase (s [3]); { Capitalize third letter of "McKay", etc. }
IF (Length (s) = 2) AND (Copy (s, 1, 2) = 'Ii') THEN
s [2] := UpCase (s [2]); { Capitalize "II" }
s2 := s2 + s;
s := s2;
s2 := '';
WHILE Pos (hyphen, s) > 0 DO BEGIN { Capitalize initial letters after hypens}
s2 := s2 + Copy (s, 1, (Pos (hyphen, s)));
Delete (s, 1, (Pos (hyphen, s)));
s [1] := UpCase (s [1]);
END;
s2 := s2 + s;
s := s2;
s2 := '';
WHILE Pos (period, s) > 0 DO BEGIN { Capitalize initial letters after periods}
s2 := s2 + Copy (s, 1, (Pos (period, s)));
Delete (s, 1, (Pos (period, s)));
s [1] := UpCase (s [1]);
END;
s2 := s2 + s;
s := s2;
MixCase := s;
END;
FUNCTION RTrim (InStr: STRING): STRING;
BEGIN
WHILE (Length (InStr) > 0) AND (InStr [Length (InStr)] IN [#0, #9, #32]) DO
system. Dec (InStr [0]);
RTrim := InStr;
END;
FUNCTION LTrim (InStr: STRING): STRING;
BEGIN
WHILE (Length (InStr) > 0) AND (InStr [1] IN [#0, #9, #32]) DO
Delete (InStr, 1, 1);
LTrim := InStr;
END;
FUNCTION Squeeze (ss: STRING): STRING;
VAR
controlCHAR: CHAR;
BEGIN
FOR controlCHAR := #0 TO #31 DO
WHILE (Ord (ss [0]) > 0) AND (Pos (controlCHAR, ss) > 0) DO
ss [Pos (controlCHAR, ss)] := #32;
ss := RTrim (LTrim (ss));
Squeeze := ss
END;
Function LongIntDays (DayStr: String): LongInt;
Var
LID : LongInt;
VErr : Integer;
Begin
DayStr := Copy(DayStr,7,2) + Copy(DayStr,1,2) + Copy(DayStr,4,2);
If DayStr[1] in ['8','9']
Then DayStr := '19'+DayStr {assume 1980-1999, rather than 2080-2099}
Else DayStr := '20'+DayStr;
Val(DayStr,LID,VErr);
If VErr <> 0
Then LongIntDays := 0
Else LongIntDays := LID
End;
FUNCTION GetNewHigh (High, current: STRING): STRING;
VAR
old, New: LONGINT;
verr: INTEGER;
BEGIN
Val (Squeeze (High), old, verr);
Val (Squeeze (current), New, verr);
IF (New > old)
THEN GetNewHigh := Squeeze (current)
ELSE GetNewHigh := High
END;
FUNCTION MiddleOf (CONST s: STRING): STRING;
VAR
pre_mid, post_mid : BYTE;
BEGIN
pre_mid := 5 * Length (s) DIV 10;
post_mid := 7 * Length (s) DIV 10;
MiddleOf := Copy (s, pre_mid, (post_mid - pre_mid))
END;
FUNCTION GetOriginLine (ol : STRING): STRING;
VAR
Pos1: BYTE;
DONE: BOOLEAN;
BEGIN
DONE := FALSE;
IF NOT DONE THEN { First search for standard QWK origin line }
REPEAT
Pos1 := Pos ('π ■', ol);
IF (Pos1 > 0) THEN
BEGIN
DONE := TRUE;
ol := Copy (ol, Pos1+1, 255); { Copy entire remaining line }
END;
UNTIL (Pos1 = 0);
IF NOT DONE THEN { Second search for standard FIDO origin line }
REPEAT
Pos1 := Pos ('π *', ol);
IF (Pos1 > 0) THEN
BEGIN
DONE := TRUE;
ol := Copy (ol, Pos1+1, 255); { Copy entire remaining line }
END;
UNTIL (Pos1 = 0);
IF NOT DONE THEN { Third search for non-standard QWK origin line }
REPEAT
Pos1 := Pos ('π■', ol);
IF (Pos1 > 0) THEN
BEGIN
DONE := TRUE;
ol := Copy (ol, Pos1+1, 255); { Copy entire remaining line }
END;
UNTIL (Pos1 = 0);
IF NOT DONE THEN { Fourth search for non-standard FIDO origin line }
REPEAT
Pos1 := Pos ('π*', ol);
IF (Pos1 > 0) THEN
BEGIN
DONE := TRUE;
ol := Copy (ol, Pos1+1, 255); { Copy entire remaining line }
END;
UNTIL (Pos1 = 0);
IF DONE THEN
BEGIN
Pos1 := Pos (#1, ol);
IF Pos1 > 0 THEN
ol := Copy (ol, 1, Pos1 - 1);
Pos1 := Pos (#227, ol); { | new with v1.52 }
IF Pos1 > 0 THEN
ol := Copy (ol, 1, Pos1 - 1);
WHILE (Ord (ol [0]) > 0) AND (ol [Length (ol)] IN [#0, #9, #32]) DO
Delete (ol, Length (ol), 1); { | changed with v1.52 }
(* WHILE (Ord (ol [0]) > 0) AND (ol [Length (ol)] IN [#0, #32, #227]) DO
Delete (ol, Length (ol), 1);
WHILE Pos (#227, ol) > 0 DO
Delete (ol, 1, Pos (#227, ol)); *)
ol := squeeze (ol);
IF (Length (ol) > 78) THEN
ol := Copy (ol, 1, 78);
GetOriginLine := #32 + ol;
END
ELSE
GetOriginLine := '';
END;
{===========================================================================}
FUNCTION GetConfNUMBER (CONST PSTR: STRING): PATHSTR;
VAR
MFNpath : PATHSTR; { MFN file path, }
MFNdir : DIRSTR; { directory, }
MFNname : NAMESTR; { name, }
MFNext : EXTSTR; { extension. }
sTemp : STRING;
index : BYTE;
VErr : INTEGER;
BEGIN
MFNpath := PSTR;
IF MFNpath [1] IN ['/', '-'] THEN Halt (1);
FSplit (FExpand (MFNpath), MFNdir, MFNname, MFNext);
IF (MFNname = '') THEN Halt (6);
IF (MFNext = '.STT') THEN Halt (3);
sTemp := '';
FOR index := 1 TO Length (MFNname) DO
IF MFNname [index] IN ['0'..'9'] THEN
sTemp := sTemp + MFNname [index];
IF sTemp = '' THEN Halt (1);
Val (sTemp, confnumb, VErr); { confnumb is a GLOBAL var }
IF VErr <> 0 THEN Halt (1);
GetConfNUMBER := MFNdir + MFNname+ MFNext;
END;
{===========================================================================}
FUNCTION IsDir (CONST FileName: PATHSTR): BOOLEAN;
VAR
Attr : WORD;
cFile : FILE;
BEGIN
Assign (cFile, FileName);
GetFAttr (cFile, Attr);
IF (DosError = 0) AND ((Attr AND Directory) = Directory)
THEN IsDir := TRUE
ELSE IsDir := FALSE;
END;
FUNCTION GetFilePath (CONST PSTR: STRING; VAR sDir: DIRSTR): PATHSTR;
VAR
jPath : PATHSTR; { file path, }
jDir : DIRSTR; { directory, }
jName : NAMESTR; { name, }
jExt : EXTSTR; { extension. }
BEGIN
jPath := PSTR;
IF jPath = '' THEN jPath := '*.*';
IF (NOT (jPath [Length (jPath)] IN [':', '\'])) AND IsDir (jPath) THEN
jPath := jPath + '\';
IF (jPath [Length (jPath)] IN [':', '\']) THEN
jPath := jPath + '*.*';
FSplit (FExpand (jPath), jDir, jName, jExt);
jPath := jDir + jName+ jExt;
sDir := jDir;
GetFilePath := jPath;
END;
{===========================================================================}
FUNCTION GetCONFname (CONST QWKpath, CNFFileName: STRING): STRING;
VAR X, Y: WORD;
CNFFile : TEXT;
CnfName,
CNameStr : STRING;
CNumb,
CNameInt : WORD;
VErr : INTEGER;
BEGIN
CnfName := '';
IF ExtractFile (QWKpath, CNFFileName, ExCMD) THEN BEGIN
Assign (CNFFile, CNFFileName);
Reset (CNFFile); CheckIO;
FOR X := 1 TO 10 DO { advance to just before number of Cnferences }
IF NOT EoF (CNFFile) THEN
ReadLn (CNFFile);
IF NOT EoF (CNFFile) THEN BEGIN
ReadLn (CNFFile, CNameStr); { get number of Cnferences }
Val (Squeeze (CNameStr), CNameInt, VErr);
IF (VErr = 0) THEN
FOR X := 0 TO CNameInt DO { walk through Cnf names }
IF NOT EoF (CNFFile) THEN BEGIN
ReadLn (CNFFile, CNameStr); { read Cnference number }
Val (Squeeze (CNameStr), CNumb, VErr);
IF (VErr = 0) AND (NOT EoF (CNFFile)) THEN BEGIN
ReadLn (CNFFile, CNameStr); { read Cnference name }
IF CNumb = ConfNumb THEN
CnfName := CNameStr
END;
END;
END;
Close (CNFFile);
EraseFile (CNFFileName);
END;
GetConfname := CnfName;
END;
{===========================================================================}
FUNCTION BuildList (VAR list: MemLink; CONST fname: STRING): WORD;
CONST
namepos = 3; sentpos = namepos + 31; oldestpos = sentpos + 14; newestpos = oldestpos + 13;
bbs1Pos = 1; bbs2Pos = 1; notespos = 7;
VAR
MemInfo : STRING;
anchor,
MemberInfo : MemLink;
infile : TEXT;
VErr : INTEGER;
Members : WORD;
DataEnd : BOOLEAN;
BEGIN
Write ('Reading membership list, please wait ... ');
DataEnd := FALSE;
Members := 0;
IF IsFile (fname) THEN BEGIN
Assign (infile, fname);
Reset (infile); CheckIO;
list := NIL;
anchor := NIL;
MemberInfo := NIL;
ReadLn (infile, MemInfo); CheckIO;
IF Copy(MemInfo,1,9) = 'CPT v1.36'
THEN Halt(8)
ELSE Reset (infile); CheckIO;
WHILE NOT DataEnd DO
BEGIN
REPEAT { find first separator line }
ReadLn (infile, MemInfo); CheckIO;
IF (Length (MemInfo) >= 15) AND (Copy (MemInfo, 1, 14) = 'High message: ') THEN
High_Message := Copy (MemInfo, 15, Length (MemInfo) - 14)
ELSE
IF (Length (MemInfo) > 50) AND (Copy (MemInfo, 1, 11) = 'Conference ') THEN
IF (Pos ('(', MemInfo) < Pos (')', MemInfo)) THEN
CONFname := Copy (MemInfo, Pos ('(', MemInfo) + 1,
Pos (')', MemInfo) - Pos ('(', MemInfo) - 1);
IF EoF (infile) OR (MemInfo = EndOfDB) THEN DataEnd := TRUE;
UNTIL (MemInfo = DelimitLine) OR DataEnd OR (MemInfo = OldDelimitLine);
IF NOT DataEnd THEN BEGIN { assume start of new data }
updatecursor;
Inc (Members);
New (MemberInfo);
WITH MemberInfo^ DO BEGIN
Name := '';
sent := 0;
oldest := '';
newest := '';
BBS1 := '';
BBS2 := '';
notes := '';
next := NIL;
END; {with}
REPEAT { fill in new data }
ReadLn (infile, MemInfo); CheckIO;
IF EoF (infile) OR (MemInfo = EndOfDB) THEN DataEnd := TRUE;
IF (NOT DataEnd) THEN
WITH MemberInfo^ DO BEGIN
IF Copy (MemInfo, 1, 2) = ': ' THEN BEGIN
Name := MixCase (Squeeze (Copy (MemInfo, namepos, SizeOf (Name))));
Val (Squeeze (Copy (MemInfo, sentpos, 4)), sent, VErr);
oldest := Copy (MemInfo, oldestpos, SizeOf (oldest));
newest := Copy (MemInfo, newestpos, SizeOf (newest));
END
ELSE IF Copy (MemInfo, 1, 6) = 'Notes:' THEN BEGIN
notes := MemInfo;
Delete (notes, 1, notespos - 1);
END
ELSE IF BBS1 = '' THEN BEGIN
BBS1 := MemInfo;
Delete (BBS1, 1, BBS1Pos - 1);
END
ELSE IF BBS2 = '' THEN BEGIN
BBS2 := MemInfo;
Delete (BBS2, 1, BBS2Pos - 1);
END
END; {with}
UNTIL DataEnd OR (Copy (MemInfo, 1, 6) = 'Notes:');
IF list <> NIL THEN
list^. next := MemberInfo
ELSE
anchor := MemberInfo;
list := MemberInfo;
END {if}
END; {while}
Close (infile); CheckIO;
ClrEol;
list := anchor;
END;
Write ('done!');
BuildList := Members;
END;
{===========================================================================}
FUNCTION ReadDAT (VAR list: MemLink; CONST DATFileName: STRING): WORD;
CONST RecSize = 128;
TYPE Buffer = ARRAY [1..RecSize] OF CHAR;
VAR
MemInfo : Buffer;
anchor, newMEM : MemLink;
NewName : STRING [25];
NextMes : WORD;
VErr : INTEGER;
CrnDate : STRING [8];
confnum : WORD;
PRIVATE : BOOLEAN;
BBStemp : STRING;
dfile : FILE;
count,
Members : WORD;
NamePos : BYTE;
BEGIN
IF CheckFROM THEN NamePos := 47
ELSE NamePos := 22;
Members := 0;
NextMes := 2;
Assign (dfile, DATFileName);
Reset (dfile, 1); CheckIO;
REPEAT
updatecursor;
FOR count := 1 TO NextMes DO BEGIN
BlockRead (dfile, MemInfo, RecSize);
IF (IOResult <> 0) THEN Continue;
END;
BBStemp := '';
Val (Squeeze (Copy (MemInfo, 117, 6)), NextMes, VErr);
IF NextMes < 1 THEN NextMes := 1;
confnum := Ord (MemInfo [125]) * 256 + Ord (MemInfo [124]);
IF TrackPrivate = TRUE THEN
PRIVATE := FALSE {Pretend *all* messages are Public}
ELSE
PRIVATE := Pos (MemInfo [1], '+*~`!#') > 0;
IF (confnum = ConfNumb) AND (NOT PRIVATE) THEN BEGIN
High_Message := GetNewHigh (High_Message, Copy (MemInfo, 2, 7));
NewName := MixCase (Squeeze (Copy (MemInfo, NamePos, 25)));
IF (Validate = FALSE) OR
((NewName <> '') AND (Pos (#0, NewName) < 1)
AND (NewName [1] IN ['A'..'Z']))
THEN BEGIN
anchor := list;
WHILE (list <> NIL) AND (list^. Name <> NewName) DO list := list^. next;
IF list = NIL THEN BEGIN
list := anchor;
Inc (Members);
New (newMEM);
WITH newMEM^ DO BEGIN
Name := NewName;
sent := 1;
oldest := Copy (MemInfo, 9, 8);
newest := oldest;
WHILE NextMes > 1 DO BEGIN
IF Length (BBStemp) > 127 THEN
Delete (BBStemp, 1, (Length (BBStemp) - 127));
BlockRead (dfile, MemInfo, RecSize); CheckIO;
IF CheckFROM THEN BBStemp := BBStemp + MemInfo;
system. Dec (NextMes);
END;
IF CheckFROM THEN BEGIN
BBStemp := GetOriginLine (BBStemp);
IF (Length (BBStemp) > 2) AND (BBStemp [2] IN [#42, #254]) THEN
BBS1 := BBStemp
ELSE
BBS1 := ' * Unknown origin';
END
ELSE
BBS1 := '';
BBS2 := '';
notes := ' !New!';
next := list;
END;
list := newMEM;
END {if list = nil then}
ELSE BEGIN {name was found}
WITH list^ DO BEGIN
sent := (sent) + 1;
CrnDate := Copy (MemInfo, 9, 8);
IF LongIntDays (CrnDate) < LongIntDays (oldest) THEN oldest := CrnDate;
IF LongIntDays (CrnDate) > LongIntDays (newest) THEN newest := CrnDate;
WHILE NextMes > 1 DO BEGIN
IF Length (BBStemp) > 127 THEN
Delete (BBStemp, 1, (Length (BBStemp) - 127));
BlockRead (dfile, MemInfo, RecSize); CheckIO;
IF CheckFROM THEN BBStemp := BBStemp + MemInfo;
system. Dec (NextMes);
END;
IF CheckFROM THEN BEGIN
BBStemp := GetOriginLine (BBStemp);
IF (Length (BBStemp) > 2) AND (BBStemp [2] IN [#42, #254]) THEN
IF (MiddleOf (BBStemp) <> MiddleOf (BBS1)) THEN
BEGIN { make BBStemp the most recent }
BBS2 := BBS1;
BBS1 := BBStemp
END
ELSE BBS1 := BBStemp;
END;
END;
list := anchor
END {if list = nil then ... else}
END {if (NewName <> '') AND (Pos(#0,NewName) < 1) ... }
END {if (confnum = ConfNumb) and (NOT private) then}
UNTIL EoF (dfile);
ClrEol;
Close (dfile); CheckIO;
ReadDat := Members;
END;
{===========================================================================}
FUNCTION Relevant (CONST s: STRING; CONST len: BYTE): STRING;
BEGIN
Relevant := Copy (s, 1, len);
END;
PROCEDURE GetSortField (CONST PSTR: STRING);
BEGIN
field := Squeeze (UpStr (PSTR));
Write ('Sorting membership list by: '+field+', please wait ... ');
IF field = '' THEN field := 'NAME';
inverse := (field [1] = '-');
IF inverse THEN Delete (field, 1, 1);
field := Relevant (field, 3);
END;
FUNCTION CompareFields (CONST cnode, cnode2: MemLink): BOOLEAN;
BEGIN
{ Originally was: (node^.name > node2^.next^.name) }
IF field = 'NAM' THEN BEGIN
IF inverse THEN
CompareFields := (cnode^. Name <= cnode2^. next^. Name)
ELSE
CompareFields := (cnode^. Name >= cnode2^. next^. Name)
END
ELSE
IF field = 'SEN' THEN BEGIN
IF inverse THEN
CompareFields := (cnode^. SENT <= cnode2^. next^. SENT)
ELSE
CompareFields := (cnode^. SENT >= cnode2^. next^. SENT)
END
ELSE
IF field = 'OLD' THEN BEGIN
IF inverse THEN
CompareFields := (LongIntDays (cnode^. OLDEST) <= LongIntDays (cnode2^. next^. OLDEST))
ELSE
CompareFields := (LongIntDays (cnode^. OLDEST) >= LongIntDays (cnode2^. next^. OLDEST))
END
ELSE
IF field = 'NEW' THEN BEGIN
IF inverse THEN
CompareFields := (LongIntDays (cnode^. NEWEST) <= LongIntDays (cnode2^. next^. NEWEST))
ELSE
CompareFields := (LongIntDays (cnode^. NEWEST) >= LongIntDays (cnode2^. next^. NEWEST))
END
END;
{===========================================================================}
PROCEDURE SortLinkedList (VAR list: MemLink); {By Ian Lin, found in SWAG}
VAR
list2, {first and second lists, temporary }
node, { Pointers to nodes in the lists }
node2 : MemLink;
BEGIN
New (list2); {begin NEW sorted list}
list2^. next := list; {steal the first node of list For list2}
list := list^. next;
list2^. next^. next := NIL;
WHILE list <> NIL DO
BEGIN {now steal 'em all and add them in order}
node := list; {point node to first node in LIST}
list := list^. next; {advance LIST Pointer one node, first node is now seperate}
node2 := list2; {ready to use NODE2 to find the correct entry point}
WHILE (node2^. next <> NIL) AND CompareFields (node, node2) DO
{ (node^.name > node2^.next^.name) }
node2 := node2^. next; {advance NODE2 as needed until it marks the
right place For NODE to be inserted}
node^. next := node2^. next; {insert NODE into the new list, in the correct order}
node2^. next := node; {connect node to the previous nodes in the new list, if any}
updateCursor;
END;
list := list2^. next; {point LIST back to the top of the list, now in order}
list2^. next := NIL;
Dispose (list2);
ClrEol;
Write ('done!');
END;
{===========================================================================}
PROCEDURE WriteList (VAR list: MemLink; CONST fname: STRING; CONST mems: WORD);
VAR
MemList : TEXT;
chain : MemLink;
sentORreceived : STRING [5];
Days : STRING[10];
BEGIN
IF CheckFROM THEN sentORreceived := 'sent '
ELSE sentORreceived := 'rcvd ';
Assign (MemList, fname);
Rewrite (MemList); CheckIO;
Write ('Writing membership list, please wait ... ');
WriteLn (MemList, 'CPT'+ version+ '(Conference Participation Tracker) text database.');
WriteLn (MemList);
WriteLn (MemList, 'Conference participation data for conference: ',confnumb,' ('+ CONFname+ ')');
WriteLn (MemList, 'Total participants: ',mems);
WriteLn (MemList, 'High message: '+ High_Message);
WriteLn (MemList);
WriteLn (MemList, ' This permanent data file may be edited, relatively freely. Beware that:');
WriteLn (MemList);
WriteLn (MemList, ' 1) The colon+space combination (: ) before each name must remain.');
WriteLn (MemList, ' 2) The position of the names and dates must not be changed.');
WriteLn (MemList, ' 3) The position of the number of messages sent must not be changed.');
WriteLn (MemList, ' 4) The label "Notes:" before the notes must not be altered,');
WriteLn (MemList, ' BUT about 70 characters of notes may be added after the label.');
WriteLn (MemList, ' 5) The delimiting lines between each participant must not be altered.');
WriteLn (MemList, ' 6) The "High message: #####" line above should be left as is.');
WriteLn (MemList, ' 7) Invalid records (5 lines per record) can and should be deleted.');
WriteLn (MemList);
WHILE list <> NIL DO BEGIN
updatecursor;
WITH list^ DO BEGIN
WriteLn (MemList, DelimitLine);
Write (MemList, ': ', Name, '': (26 - Length (Name)),
sentORreceived, sent: 4, ', between '+oldest+' and '+newest);
Str ((1 + (Num_Days (newest) - Num_Days (oldest))), Days);
if Days = '1'
then Days := Days + ' day)'
else Days := Days + ' days)';
WriteLn (MemList, ' (', Days);
WriteLn (MemList, bbs1);
WriteLn (MemList, bbs2);
WriteLn (MemList, 'Notes:'+ notes);
END;
chain := list;
list := list^. next;
Dispose (chain);
END;
WriteLn (MemList, EndOfDB);
ClrEol;
Close (MemList); CheckIO;
Write ('done!');
END;
{===========================================================================}
PROCEDURE WriteStats (VAR list: MemLink; CONST fname: STRING; CONST mems: WORD);
CONST
Header = 'Name ' +
'Sent Oldest Newest Days Avg.';
VAR
MemList : TEXT;
chain : MemLink;
TotalSent : LONGINT;
count,
rank,
LastSent : WORD;
days : WORD;
BEGIN
Assign (MemList, fname);
Rewrite (MemList); CheckIO;
Write ('Writing membership list, please wait ... ');
TotalSent := 0;
chain := list;
WHILE (list <> NIL) DO BEGIN
Inc(TotalSent,list^.sent);
list := list^. next;
END;
list := chain;
WriteLn (MemList);
WriteLn (MemList, ' Conference participation stats for conference: ', confnumb, ' ('+ CONFname+ ')');
WriteLn (MemList, ' Number of participants: ', mems);
IF TrackPrivate = TRUE THEN
WriteLn (MemList, ' Total messages counted: ', TotalSent)
ELSE
WriteLn (MemList, ' Public messages posted: ', TotalSent);
WriteLn (MemList);
IF (field = 'SEN') AND inverse THEN BEGIN
count := 0;
rank := 1;
LastSent := 65535;
WriteLn (MemList, 'Rank '+Header);
Write (MemList, '~~~~~~~');
END
ELSE
WriteLn (MemList, Header);
WriteLn (MemList, Copy (DelimitLine, 1, 63));
WHILE (list <> NIL) DO BEGIN
updatecursor;
WITH list^ DO BEGIN
IF (field = 'SEN') AND inverse THEN BEGIN
Inc (count);
IF sent <> LastSent THEN BEGIN
rank := count;
LastSent := sent
END;
Write (MemList, rank: 4, ': ');
END;
Write (MemList, Name, '': (26 - Length (Name)), sent: 4, oldest: 11, newest: 11);
days := 1 + Num_Days (newest) - Num_Days (oldest);
Write (MemList, days: 5);
WriteLn (MemList, (sent / days): 6: 2);
END;
chain := list;
list := list^. next;
Dispose (chain);
END;
WriteLn (MemList);
WriteLn (MemList, '[end of CPT statistics]');
ClrEol;
Close (MemList); CheckIO;
Write ('done!');
END;
{===========================================================================}
PROCEDURE InitCONFIG;
VAR
cpath : PATHSTR; {cpath, etc fully qualified pathnames of *.cfg files}
cdir : DIRSTR;
cname : NAMESTR;
cext : EXTSTR;
CfgFile: TEXT;
CfgLine,
CfgVar, CfgVal: PATHSTR;
equalPos: BYTE;
BEGIN
FSplit (FExpand (ParamStr(0)), cdir, cname, cext); { break up path into components }
cpath := cdir + cname + '.cfg';
CONFname := '';
Validate := TRUE;
TrackPrivate := FALSE;
unQWK := 'gus';
unARC := 'pkxarc';
unARJ := 'arj e -y';
unHAP := 'pah e';
unLHA := 'lha e';
unPAK := 'pak e /wa';
unRAR := 'rar e';
unUC2 := 'uc e -f';
unZIP := 'pkunzip -# -o';
unZOO := 'zoo -extract';
IF UpStr (cname) = 'CPT-T'
THEN CheckFROM := FALSE
ELSE CheckFROM := TRUE;
IF IsFile (cpath) THEN
BEGIN
Assign (CfgFile, cpath);
Reset (CfgFile); CheckIO;
WHILE NOT SeekEoF (CfgFile) DO
BEGIN { find vars }
ReadLn (CfgFile, CfgLine);
equalPos := Pos ('=', CfgLine);
IF (equalPos > 1) AND (Length (CfgLine) > 8) THEN BEGIN
CfgVar := Squeeze (UpStr (Copy (CfgLine, 1, equalPos - 1)));
CfgVal := Squeeze (UpStr (Copy (CfgLine, equalPos + 1, Length (CfgLine) - equalPos)));
IF (CfgVar = 'VALIDATE') THEN
BEGIN
IF CfgVal = 'FALSE' THEN Validate := FALSE;
END
ELSE IF (CfgVar = 'TRACKPRIVATE') THEN
BEGIN
IF CfgVal = 'TRUE' THEN TrackPrivate := TRUE;
END
ELSE IF (CfgVar = 'UNQWK') THEN
unQWK := CfgVal
ELSE IF (CfgVar = 'UNARC') THEN
unARC := CfgVal
ELSE IF (CfgVar = 'UNARJ') THEN
unARJ := CfgVal
ELSE IF (CfgVar = 'UNHAP') THEN
unHAP := CfgVal
ELSE IF (CfgVar = 'UNLHA') THEN
unLHA := CfgVal
ELSE IF (CfgVar = 'UNPAK') THEN
unPAK := CfgVal
ELSE IF (CfgVar = 'UNRAR') THEN
unRAR := CfgVal
ELSE IF (CfgVar = 'UNUC2') THEN
unUC2 := CfgVal
ELSE IF (CfgVar = 'UNZIP') THEN
unZIP := CfgVal
ELSE IF (CfgVar = 'UNZOO') THEN
unZOO := CfgVal
END;
END; { loop back to read another line }
Close (CfgFile);
END;
END;
{===========================================================================}
FUNCTION IsArchive (CONST SomeFile: PATHSTR): PATHSTR;
VAR
ExCMD : PATHSTR;
ExWord: NAMESTR;
FileID : ARCTYPE;
BEGIN
ExCMD := '';
FileID := IsArc (SomeFile);
IF FileID <> NONE THEN BEGIN
IF FileID = ARC THEN ExCMD := unARC ELSE
IF FileID = ARJ THEN ExCMD := unARJ ELSE
IF FileID = LZH THEN ExCMD := unLHA ELSE
IF FileID = HAP THEN ExCMD := unHAP ELSE
IF FileID = PAK THEN ExCMD := unPAK ELSE
IF FileID = RAR THEN ExCMD := unRAR ELSE
IF FileID = UC2 THEN ExCMD := unUC2 ELSE
IF FileID = ZIP THEN ExCMD := unZIP ELSE
IF FileID = ZOO THEN ExCMD := unZOO
ELSE
BEGIN
ExCMD := unQWK;
END;
IF (Pos (#32, ExCMD) IN [2..9])
THEN ExWord := Copy (ExCMD, 1, Pos (#32, ExCMD) - 1)
ELSE ExWord := Copy (ExCMD, 1, 8);
Write ('(Trying "', ExWord, '") ');
END;
IsArchive := ExCMD;
END;
FUNCTION ExtractFile (CONST ArchiveFile, FileToEx: PATHSTR; ExCMD: PATHSTR): BOOLEAN;
VAR
X, Y, newX: BYTE;
BEGIN
X := WhereX;
Y := WhereY;
ExCMD := ExCMD + #32 + ArchiveFile + #32 + FileToEx;
{$IFDEF DPMI}
SwapVectors;
Exec (GetEnv ('COMSPEC'), ' /c '+ExCMD+' >nul');
SwapVectors;
{$ELSE}
DosError := HeapMan. Execute (GetEnv ('COMSPEC'), ' /c '+ExCMD+' >nul');
{$ENDIF}
newX := WhereX;
IF (Y = WhereY) and (WhereX >= newX) THEN
BEGIN {If we haven't moved to a new line... }
GotoXY (X, Y); {return to where we were at start of procedure}
ClrEol;
END;
cursorOff;
ExtractFile := IsFile (FileToEx)
END;
{===========================================================================}
BEGIN
cursorOff;
InitConfig;
END.